#pragma once

#include <iostream>
#include <poll.h>
#include "Log.hpp"
#include "Socket.hpp"

class PollServer
{
public:
    static const uint16_t default_port = 8080;
    static const int default_fd = -1;
    static const int fd_max_num = 64; // 定义变量存储，方便修改
    static const int non_event = 0;

    PollServer(uint16_t port = default_port)
        : _port(port)
    {
        for (int i = 0; i < fd_max_num; ++i)
        {
            _fds[i].fd = default_fd;
            _fds[i].events = non_event;
            _fds[i].revents = non_event;
        }
    }

    void Initial()
    {
        _listensock.CreateSock();
        _listensock.Bind(_port);
        _listensock.Listen();
        lg(Info, "create listen sock success, fd: %d", _listensock.GetSockFd());
    }

    void Accept()
    {
        std::string clientip;
        uint16_t clientport;
        int fd = _listensock.Accept(&clientip, &clientport);
        // 当Accept失败时，不再执行下面的操作
        if (fd < 0)
            return;
        lg(Info, "accept success, [%s:%d] fd: %d", clientip.c_str(), clientport, fd); // ip地址与端口号顺便输出
        int pos = 0;                                                                  // 写为pos更标准些
        for (; pos < fd_max_num; ++pos)
        {
            if (_fds[pos].fd != default_fd)
                continue;
            else // 直接退出，在下面再做处理
                break;
        }
        if (pos == fd_max_num)
        {
            lg(Warning, "select fd array is full, fd: %d close", fd);
            close(fd);
            // 可考虑扩容
        }
        else
        {
            _fds[pos].fd = fd;
            _fds[pos].events = POLLIN;
            _fds[pos].revents = non_event;
            PrintAllFd();
        }
    }

    void Receive(int fd, int pos)
    {
        char buffer[1024];
        ssize_t sz = read(fd, buffer, sizeof(buffer) - 1);
        if (sz > 0)
        {
            buffer[sz] = 0;
            std::cout << "server get a message: " << buffer << std::endl;
            // std::string echo_str = "server: ";
            // echo_str += buffer;
            // sz = write(fd, echo_str.c_str(), echo_str.size());
            // if(sz < 0)
            //     lg(Error, "fd: %d, write error", fd);
        }
        else
        {
            if (sz == 0)
                lg(Info, "client quit, fd: %d close", fd);
            else
                lg(Error, "read error, fd: %d, errno: %d, error string: %s", fd, errno, strerror(errno)); // fd顺便也输出

            // 写在一起，不会那么冗余
            close(fd); // 不需要使用时，别忘了关闭
            _fds[pos].fd = default_fd;
            _fds[pos].events = non_event;
            _fds[pos].revents = non_event;
        }

        // else if (sz == 0)
        // {
        //     lg(Info, "client quit, fd: %d close", fd);
        //     close(fd); // 不需要使用时，别忘了关闭
        //     _fds[pos].fd = default_fd;
        //     _fds[pos].events = non_event;
        //     _fds[pos].revents = non_event;
        // }
        // else
        // {
        //     lg(Error, "read error, fd: %d, errno: %d, error string: %s", fd, errno, strerror(errno)); // fd顺便也输出
        //     close(fd);
        //     _fds[pos].fd = default_fd;
        //     _fds[pos].events = non_event;
        //     _fds[pos].revents = non_event;
        // }
    }

    void Dispatch()
    {
        for (int i = 0; i < fd_max_num; ++i)
        {
            // int listensock = _listensock.GetSockFd();        // 只用一次，无需单独设置一个变量
            int fd = _fds[i].fd; // 需多次使用，可用变量保存
            if (fd == default_fd)
                continue;
            if (_fds[i].revents & POLLIN) //_fds[i]要写全，不要写成_fds->revents。否则，只能接受一次read，接下来的消息只能等有新的连接到来才能收到
            {
                if (fd == _listensock.GetSockFd())
                    Accept();
                else
                    Receive(fd, i);
            }
            else // out或者其他情况
            {
            }
        }
    }

    void Start()
    {
        _fds[0].fd = _listensock.GetSockFd();
        _fds[0].events = POLLIN; // [0]别没写，是对监听套接字进行设置
        int timeout = 3000;
        for (;;)
        {
            int n = poll(_fds, fd_max_num, timeout);
            switch (n)
            {
            case -1:
                lg(Error, "poll error");
                break;
            case 0:
                lg(Info, "timeout");
                break;
            default:
                lg(Info, "poll waite success, ready num: %d", n);
                Dispatch();
                break;
            }
        }
    }

    void PrintAllFd()
    {
        std::cout << "online fd: ";
        for (int i = 0; i < fd_max_num; ++i)
        {
            if (_fds[i].fd == default_fd)
                continue;
            std::cout << _fds[i].fd << " ";
        }
        std::cout << std::endl;
    }

    ~PollServer()
    {
        _listensock.Close();
    }

private:
    Socket _listensock;
    uint16_t _port;
    struct pollfd _fds[fd_max_num];
};